#include "wl_malloc.h"

#include "tinyalloc.h"
#include "wl_ext_os.h"
#include "wl_ext_mem.h"
#include "wl_trace.h"

#ifdef __cplusplus
extern "C" {
#endif

#if defined(WL_MEMORY_POOL)

//内存池分配

#    define WL_MEMORY_POOL_SPLIT_THRESH 40  //内存切片最小值
#    define WL_MEMORY_POOL_ALIGNMENT 4      // 4字节对齐
#    define WL_MEMORY_POOL_BLOCKS (2000)    //内存池块数(注意群好友数量,JSON对内存块要求比较多)

typedef struct {
    wl_bool  initialized;
    void*    memory_pool;
    wl_ext_mutex mutex;
} wl_mem_global;

wl_mem_global s_mem_global;

wl_bool wl_mem_init() {
    if (!s_mem_global.initialized) {
        //初始化内存池
        s_mem_global.memory_pool = wl_ext_malloc(WL_MEMORY_POOL_SIZE);
        if (!ta_init((Heap*)s_mem_global.memory_pool,
                     (const void*)((wl_char*)s_mem_global.memory_pool + WL_MEMORY_POOL_SIZE),
                     WL_MEMORY_POOL_BLOCKS,
                     WL_MEMORY_POOL_SPLIT_THRESH,
                     WL_MEMORY_POOL_ALIGNMENT)) {
            WLLOGW("ta_init fail");
            return wl_false;
        }

        if (0 != wl_ext_mutex_create(&s_mem_global.mutex, "malloc")) {
            WLLOGW("wl_mutex_create fail");
            wl_ext_free(s_mem_global.memory_pool);
            return wl_false;
        }
        s_mem_global.initialized = wl_true;
        return wl_true;
    } else {
        WLLOGW("memory pool has init");
        return wl_true;
    }
}

void wl_mem_end(void) {
    if (s_mem_global.initialized) {
        if (wl_null != s_mem_global.memory_pool) {
            wl_ext_free(s_mem_global.memory_pool);
        }
    
        wl_ext_mutex_destroy(s_mem_global.mutex);
        s_mem_global.initialized = wl_false;
    }
}

#    ifdef WL_MALLOC_CHECK
void* wl_mem_malloc_debug(wl_size size, const wl_char* func, wl_uint32 line) {
    void* ptr = wl_null;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    ptr = ta_alloc_debug(s_mem_global.memory_pool, size, func, line);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    WLLOGT("mem_malloc ptr %p size %d func %s line %d", ptr, size, func, line);
    if (wl_null == ptr) {
        WLLOGW("mem malloc size %d fail", size);
    }
    return ptr;
}

void wl_mem_free_debug(void* ptr, const wl_char* func, wl_uint32 line) {
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    ta_free(s_mem_global.memory_pool, ptr);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    WLLOGT("mem_free ptr %p func %s line %d", ptr, func, line);
}
#    endif

void* wl_mem_malloc(wl_size size) {
    void* ptr = wl_null;
    if (size > WL_MEMORY_SIZE_LIMIT) {
        if (size > (wl_mem_fresh_size() + wl_mem_free_size()) / 2) {
            WLLOGW("malloc size %d too large fresh %d size %d", size, wl_mem_fresh_size(), wl_mem_free_size());
            return wl_null;
        }
    }
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    ptr = ta_alloc(s_mem_global.memory_pool, size);
    wl_ext_mutex_unlock(s_mem_global.mutex);
#    ifdef WL_MALLOC_CHECK
    WLLOGT("mem_malloc ptr %p size %d", ptr, size);
#    endif
    if (wl_null == ptr) {
        WLLOGW("malloc size %d mem used %d size %d fresh %d size %d free %d size %d sys size %d",
               size,
               wl_mem_num_used(),
               wl_mem_used_size(),
               wl_mem_num_fresh(),
               wl_mem_fresh_size(),
               wl_mem_num_free(),
               wl_mem_free_size(),
               wl_mem_sys_size());
    }
    return ptr;
}

void wl_mem_free(void* ptr) {
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    ta_free(s_mem_global.memory_pool, ptr);
    wl_ext_mutex_unlock(s_mem_global.mutex);
#    ifdef WL_MALLOC_CHECK
    WLLOGT("mem_free ptr %p", ptr);
#    endif
}

void* wl_mem_realloc(void* ptr, wl_size size) {
    void* new_ptr = wl_null;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    new_ptr = ta_alloc(s_mem_global.memory_pool, size);
    if (new_ptr) {
        wl_size ptr_size = ta_mem_size(s_mem_global.memory_pool, ptr);
        memset(new_ptr, 0, size);
        if (ptr_size) {
            memcpy(new_ptr, ptr, ptr_size > size ? size : ptr_size);
        }
    } else {
        WLLOGW("mem realloc size %d fail", size);
    }
    ta_free(s_mem_global.memory_pool, ptr);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return new_ptr;
}

wl_size wl_mem_size(void* ptr) {
    wl_size size = 0;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    size = ta_mem_size(s_mem_global.memory_pool, ptr);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return size;
}

wl_size wl_mem_num_free(void) {
    wl_size size = 0;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    size = ta_num_free(s_mem_global.memory_pool);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return size;
}

wl_size wl_mem_num_used(void) {
    wl_size size = 0;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    size = ta_num_used(s_mem_global.memory_pool);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return size;
}

wl_size wl_mem_num_fresh(void) {
    wl_size size = 0;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    size = ta_num_fresh(s_mem_global.memory_pool);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return size;
}

wl_size wl_mem_free_size(void) {
    wl_size size = 0;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    size = ta_free_size(s_mem_global.memory_pool);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return size;
}

wl_size wl_mem_used_size(void) {
    wl_size size = 0;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    size = ta_used_size(s_mem_global.memory_pool);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return size;
}

wl_size wl_mem_fresh_size(void) {
    wl_size size = 0;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    size = ta_fresh_size(s_mem_global.memory_pool);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return size;
}

wl_bool wl_mem_check(void) {
    wl_bool ret = wl_false;
    wl_ext_mutex_lock(s_mem_global.mutex, WL_EXT_WAIT_FOREVER);
    ret = ta_check(s_mem_global.memory_pool);
    wl_ext_mutex_unlock(s_mem_global.mutex);
    return ret;
}
#else
wl_bool wl_mem_init(void) {
    return wl_true;
}

void wl_mem_end(void) {
}

void* wl_mem_malloc(wl_size size) {
#    ifdef WL_MALLOC_DEBUG
    void* ptr = wl_ext_malloc(size + sizeof(wl_size));
    if (wl_null == ptr) {
        WLLOGW("mem malloc size %d fail", size);
    }
    (*(wl_size*)ptr) = size;
    return (void*)(ptr + sizeof(wl_size));
#    else
    void* ptr = wl_ext_malloc(size);
    if (wl_null == ptr) {
        WLLOGW("mem malloc size %d fail", size);
    }
    return ptr;
#    endif
}

void wl_mem_free(void* ptr) {
#    ifdef WL_MALLOC_DEBUG
    void* origin_ptr = (void*)((wl_int8*)ptr - sizeof(wl_size));
    wl_ext_free(origin_ptr);
#    else
    wl_ext_free(ptr);
#    endif
}

void* wl_mem_realloc(void* ptr, wl_size size) {
#    ifdef WL_MALLOC_DEBUG
    if (ptr) {
        void* old_ptr = ((void*)ptr - sizeof(wl_size));
        void* new_ptr = wl_ext_realloc(old_ptr, size + sizeof(wl_size));
        if (wl_null == new_ptr) {
            WLLOGW("mem realloc size %d fail", size);
        }
        (*(wl_size*)new_ptr) = size;
        return (void*)(new_ptr + sizeof(wl_size));
    } else {
        return wl_mem_malloc(size);
    }
#    else
    void* new_ptr = wl_ext_realloc(ptr, (size_t)size);
    if (wl_null == new_ptr) {
        WLLOGW("mem realloc size %d fail", size);
    }
    return new_ptr;
#    endif
}

wl_size wl_mem_size(void* ptr) {
    void* origin_ptr = ((void*)ptr - sizeof(wl_size));
    return (*(wl_size*)origin_ptr);
}

wl_size wl_mem_num_free(void) {
    return 0;
}

wl_size wl_mem_num_used(void) {
    return 0;
}

wl_size wl_mem_num_fresh(void) {
    return 0;
}

wl_size wl_mem_free_size(void) {
    return 0;
}

wl_size wl_mem_used_size(void) {
    return 0;
}

wl_size wl_mem_fresh_size(void) {
    return ql_rtos_get_free_heap_size();
}

wl_bool wl_mem_check(void) {
    return true;
}
#endif

wl_size wl_mem_sys_size(void) {
    return wl_ext_mem_freesize();
}

#ifdef __cplusplus
}
#endif
